Welcome back.
In this lecture we're going to finally start utilizing the OOP concepts we've just learned about.
In this lecture.
We're going to create this toxic gas cloud here I'll demonstrate it for you real quick.
And when we go to enter this toxic gas cloud, as you can see, it starts damaging us.
And when we leave it, it stops.
And when we go back in, it damages us again.
We're going to be utilizing encapsulation and abstraction in this lecture, as well as learning about
a function in the workspace called git parts and part.
So without further ado, let's get started.
So the first thing you'll want to do is you'll want to grab this model that I've attached to the lecture.
And it's just a part that has a particle emitter and a properties module script inside of it.
So this is the particle emitter that will emit the smoke of our toxic gas cloud.
And inside of the properties module script here, it just lays out how much damage that this gas cloud
will deal per this tick rate.
So it'll deal 15 damage every 0.5 seconds.
I've placed this gas cloud in a folder called gas clouds.
That way my workspace is more organized.
But in the next lecture we'll learn about the collection service so you don't have to keep related things
in your game all in the same folder.
So let's get started creating the class for our toxic gas cloud.
I have this module script here and it's going to be acting as my class.
Now.
Firstly, to uphold the principle of encapsulation, we have to have two tables in our class, one that's
going to store the constructor, while the other is going to be the meta table holding the functions
that will be used on the objects themselves.
This way we can correctly encapsulate the functions to their correct tables.
So in this section here it's going to be for my variables.
And I'm going to create a table for my gas cloud class.
And then I'm also going to create a meta table for the gas cloud class.
And I want my gas cloud to have some basic properties, such as the part that is representing the gas
cloud.
We'll set it to no.
For now, we'll want to have a reference to the particle emitter that's in the gas cloud.
We'll want to know whether or not this gas cloud is enabled.
So we'll set this to false by default.
And then we want access to the properties that are inside of our gas cloud.
And we're going to set the properties to nil for now as well.
We're also going to make sure to set the gas clouds underscore underscore index meta method to itself.
And I'm also going to create another table.
I'll call it current gas clouds.
And it's just going to represent all the objects that I have in my game.
So any gas clouds I have will be stored in here.
All of the tables will be stored in this one.
Next, we're going to need some private functions for our toxic gas cloud class.
And these are going to be private functions.
So we uphold abstraction, meaning we're hiding away details from people that are requiring or using
this class one function we'll need.
Is to get all of the humanoids that are going to be inside of this part.
So whenever a player is going to be inside of this part, we'll use a function to check for all the
parts in this part.
And then we're going to get all the humanoids of each character or player that's inside of this toxic
gas cloud, and damage them accordingly.
So we'll call this function get humanoids and part and we'll pass a part to this function.
We'll leave it empty for now.
We'll also need a function to run the damaging tick while the toxic gas cloud is active.
So we can call it damage tick.
And we'll be passing um, we'll pass self to this, which is going to represent the object.
And in here we'll run a loop in a separate thread using task dot spawn to repeatedly damage and check
for any humanoids within the gas cloud every, uh, however long the tick rate is.
Otherwise, this section is going to store all my public functions, such as my constructor and all
the functions that can be called on the gas clouds.
So we can call this one gas cloud dot new.
And this constructor, remember, has to belong to this gas cloud cable, not to our gas cloud meta
table.
And what we need to pass here, I would say, is just the gas part or the part that's going to be representing
our gas cloud.
And then from that point in here, we could create a table.
We'll call it self equal to set meta table, create a new table and attach the gas cloud meta table
to it.
And then from that point, we can access all of these default properties and override them to whatever
this object is going to be.
So the part is going to be this gas part.
The emitter is going to be equal to the gas part, and we're going to get or we're going to find first
child, which is a particle emitter.
That way we can name the particle emitter whatever we'd like, but we can still get it because it's
part of the particle emitter class.
Set enabled to false.
Actually, it's already set to false by default, so we don't have to set it here.
But we can do properties equal to require.
Gas part properties.
So we're grabbing the properties table and storing it here in this property.
From that point, we can keep track of this object by inserting it inside of our current gas clouds
table.
That way, let's say for some reason, we need a function to get all of the current gas clouds.
We can do so.
So we're going to insert it into this.
Table.
And then once that's done, we can just return this table back to where this function was called.
And in fact, let's make another function inside of the gas cloud table.
I'm going to call it get gas clouds.
And it's just going to return the current gas clouds table.
So if at any time when I'm using this class, I want to get all of the gas clouds that are currently
in my game and be able to enable and disable ones I want, then this will allow me to do so.
Otherwise, we're going to need some functions that are going to belong to our meta table.
And these functions are going to be called directly on these objects themselves.
So one function would be like gas cloud meta table.
Uh, we could do enable to enable the gas cloud because they're not enabled by default.
And this one can be pretty simple.
We can first check if self dot enabled is true.
If it is we'll just return.
Otherwise we can set self dot enable to true.
We can set the emitter.
To true.
So it's emitting particles.
And then we can call that damaged tick function passed self to it.
So that way the gas cloud starts checking for when any players enter inside of it.
And then we could do the same thing for disabling the gas cloud.
Call it disable.
And again we'll check if the object is already disabled, then we'll just return.
Otherwise we'll set enable to false and disable the emitter.
And that seems pretty good.
Then down here we can just have a section called main and we're just going to return.
The gas cloud table.
Now let's go ahead and fill out our damage tick and get humanoids in part function.
So for the damage tick function, what I want to do is I want to use the task dot spawn function to
spawn a new thread.
And what I'll do in here is I'm going to loop constantly.
So while self dot enabled.
So while this gas cloud is enabled what we're going to do.
Is we're going to check for any humanoids in the gas cloud so we can do local humanoids equal to the
get humanoids in part.
So with this function, we can return a table back here that stores all of the humanoids that are inside
of the gas cloud.
So let's fill out that function real quick.
I'll pass the self dot part to this function.
And then inside of this function what we'll do.
Is we can get all the parts that are inside of this part using a function in the workspace called git
parts and part, and it returns an array of parts whose occupied space is shared with the given part,
and we'll pass the part that is passed to the function.
And then we can create a table to store all the humanoids that we find inside of this part.
So what I can do is loop through every single part that is in I pairs parts.
And actually I'll just call this P.
That way we don't confuse it with this part variable up here.
We can check, um, if there's a humanoid.
So we'll make a variable called humanoid.
And we'll set it equal to the part dot parent.
And then we can find first child which is a humanoid.
So if this part is a torso, a head or whatever, the parent should be the player's character and the
character should have a child that is a humanoid.
However, if there is no humanoid, then we can just return because we don't care about this part.
Otherwise, if there is a humanoid, then we also need to make sure that this humanoid isn't already
stored within the humanoids table, because there's going to be multiple parts of a player inside of
our part, like each limb, the head.
And this will run multiple times, and it might store that player's humanoid multiple times, which
we don't want to happen.
So we can use table, dot, find and check to see if this humanoid already exists within the humanoid
table.
If it does, then we're just going to continue looping.
And actually right here we don't want to return.
We want to continue looping through all of the parts.
If we return, it's going to exit out of this for loop prematurely, and we won't be able to check all
the parts.
So we're going to do continue and not return.
Otherwise, if it matches all the criteria, then we can insert this humanoid into the humanoids table.
And then at the very end of this function, we're going to return humanoids.
Great.
So now we have the humanoids table, all the humanoids that are inside of our gas cloud.
What we could do is loop through each one of those humanoid.
And we can use a function on the humanoid called take damage.
And the amount of damage we want to take is the self dot properties dot and it's called damage per tick.
And then at the very end of this while loop, we can wait for the self dot properties dot tick rate.
So currently it's at 0.5.
So this loop is going to run every 0.5 seconds.
And it's going to damage each player by the damage per tick which I believe was yeah 15.
So now I think this class is pretty good.
We've encapsulated the functions to belong to two different tables.
So that way we can't use the constructor from the objects themselves because they have a different meta
table attached to them.
And we've also upheld the principle of abstraction by hiding the internal workings of our class to these
local functions, because we don't want to store them in the table, because anybody using this module
script doesn't need to see these functions in the first place.
We've also kept this table here hidden as well.
And if anybody wants to access to this table, they have to call a function called get gas clouds.
So again we are encapsulating and we are abstracting our code here.
Now I have another script that I made, and this script is going to require this module script and go
through every single gas cloud that I have in this folder and create new objects for each.
And I'm going to enable all of these gas clouds.
So what we need to do is we need to get a service depending on where you stored your toxic gas cloud
module script.
But mine stored in server storage.
So I'm going to get it from server storage.
And then we can have a section here for variables.
So I'm going to get the glass cloud class.
I'm going to require it from server storage.
I have a folder called classes and it's in there called Toxic Gas Cloud.
So now I have required my module script.
And that means all the code in it has ran.
And now I can also reference the folder where all the gas clouds will be.
I'll call it gas clouds and it's equal to workspace dot gas clouds.
From this point I can have a section called main, because what I'm going to do is I'm going to loop
through every single gas cloud that is in the gas clouds folder.
So gas clouds get children.
What we'll do is we'll use the gas cloud class and create a new gas cloud and pass this part to it.
And then once that's done and this function returns back to me the table or object that represents this
gas cloud, I can call the enable function on it.
So I enable this gas cloud.
So that means if I go and run my game this part should start emitting particles.
And there we go.
Our gas cloud looks to be working and it's emitting all of the particles.
So let's actually go ahead and click play here.
And we should be able to go inside of our gas cloud and start having damage dealt to us.
So there we go.
We're losing damage every 0.5 seconds and it's 15 damage per tick.
So very nice.
And now because I've implemented that get gas clouds function, what I could do is I could wait 10s
here.
And then I could get every single gas cloud that is in my game, so I could loop through every single
gas cloud and I pairs.
We're going to reference the gas cloud class and get every single gas cloud.
And then what I could do with that is I could disable this gas cloud.
Disable.
And now that means after 10s, every single gas cloud in my game should be disabled.
So let's go ahead and actually duplicate this gas cloud multiple times because I can have multiple objects.
And we can go and play test the game here.
And we've got our gas clouds running and we're taking damage.
Still perfect.
Oh, looks like we're not taking damage within this one.
And, oh, it looks like the gas clouds have been disabled.
And that's why.
There we go.
The gas clouds have been disabled and we no longer take any damage.
Um, let's make sure all of that is working.
So I'm going to play again.
So let me go in this one, okay, I take damage, I go in this one.
Perfect.
Take damage.
And a few more seconds they should disable.
There we go.
No more particles and we do not take any more damage.
As you can see, this is an example of how we can use module scripts in object oriented programming
to create scalable and maintainable code for things in our game, like toxic gas clouds.
I hope you learned a lot.
And in the next lecture, we're going to be learning about how to use composition and the collection
service to create land mines.
So I'll see you there.